package com.xuecheng.search.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.xuecheng.commons.enums.ErrorCode;
import com.xuecheng.commons.model.dto.CoursePubIndexDto;
import com.xuecheng.commons.model.dto.PageRequest;
import com.xuecheng.commons.model.vo.CoursePubIndexVo;
import com.xuecheng.commons.model.vo.CoursePubVo;
import com.xuecheng.commons.model.vo.PageResponseResult;
import com.xuecheng.commons.model.vo.ResponseResult;
import com.xuecheng.commons.utils.BeanHelper;
import com.xuecheng.content.api.ContentClient;
import com.xuecheng.search.domain.CoursePubIndex;
import com.xuecheng.search.service.SearchService;
import com.xuecheng.web.exception.BusinessException;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

@Service
public class SearchServiceImpl implements SearchService {

    @Autowired
    private ContentClient contentClient;

    @Autowired
    private RestHighLevelClient client;

    // 数据同步
    @Override
    public void MysqlToES(Long coursePubId) throws Exception {
        // 1.feign调用查询课程发布对象
        ResponseResult<CoursePubVo> responseResult = contentClient.findById(coursePubId);
        if (responseResult.getCode() != 0) { // 调用失败
            throw new BusinessException(ErrorCode.NOTFOUND);
        }
        CoursePubVo coursePubVo = responseResult.getData();
        // 2.转成索引实体类
        CoursePubIndex coursePubIndex = BeanHelper.copyProperties(coursePubVo, CoursePubIndex.class);
        coursePubIndex.setIs_pub(coursePubVo.getIsPub());
        coursePubIndex.setCourse_id(coursePubVo.getCourseId());
        coursePubIndex.setIs_latest(coursePubVo.getIsLatest());
        coursePubIndex.setCreate_date(new Date());
        coursePubIndex.setCompany_id(coursePubVo.getCompanyId());
        coursePubIndex.setCompany_name(coursePubVo.getCompanyName());
        coursePubIndex.setChange_date(new Date());
        coursePubIndex.setComment_num(0L); // 评论数量

        // 价格手动设置  单位分
        coursePubIndex.setPrice(coursePubVo.getPrice().floatValue()*100);

        // 3.保存ES
        // 3-1 创建request
        IndexRequest request = new IndexRequest("course").id(coursePubIndex.getId().toString());
        // 3-2 准备DSL
        request.source(JSON.toJSONString(coursePubIndex), XContentType.JSON);
        // 3-3 发送请求
        client.index(request, RequestOptions.DEFAULT);

    }

    // 分页条件查询
    @Override
    public ResponseResult<CoursePubIndexVo> search(PageRequest pageRequest, CoursePubIndexDto dto) throws IOException {
        //1.创建SearchRequest
        SearchRequest request = new SearchRequest("course");
        //2.准备DSL
        buildDSL(pageRequest, dto, request);
        //3.发送请求
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        //4.解析结果
        return getResponseResult(response);
    }

    // 根据id查询课程(免费、收费)
    @Override
    public ResponseResult<CoursePubIndexVo> findById(String id) throws IOException {
        //1.创建getRequest
        GetRequest request = new GetRequest("course").id(id);
        //2.发送请求
        GetResponse response = client.get(request, RequestOptions.DEFAULT);
        //3.返回结果
        //3-1取出JSON
        String json = response.getSourceAsString();
        //3-2转vo
        CoursePubIndexVo vo = JSON.parseObject(json, CoursePubIndexVo.class);
        //3-3返回
        return ResponseResult.okResult(vo);
    }

    //抽取解析响应代码
    private ResponseResult getResponseResult(SearchResponse response) {
        //4.解析结果并返回
        //4-1 取出命中结果
        SearchHits hits = response.getHits();
        //4-2取出总记录数
        long total = hits.getTotalHits().value;
        //4-3取出结果集数组遍历
        SearchHit[] searchHits = hits.getHits();
        //声明voList集合
        List<CoursePubIndexVo> voList = new ArrayList<>();
        if (ArrayUtil.isNotEmpty(searchHits)) {
            for (SearchHit searchHit : searchHits) {
                //获取原始JSON稳定的
                String json = searchHit.getSourceAsString();
                // 转为java的vo对象、
                CoursePubIndexVo vo = JSON.parseObject(json, CoursePubIndexVo.class);
                //单独指定indexId
                vo.setIndexId(vo.getCourseId()); //跳转详情页所使用的
                //------------------处理高亮 start
                //a) 取出map
                Map<String, HighlightField> highlightFields = searchHit.getHighlightFields();
                //b) map非空判断
                if (CollectionUtil.isNotEmpty(highlightFields)) {
                    //c)取出name数组
                    HighlightField nameArray = highlightFields.get("name");
                    //d)对name数组进行判断
                    if (nameArray != null) {
                        String name = nameArray.getFragments()[0].toString();
                        //e)将高亮设置到vo实体中
                        vo.setName(name);
                    }
                }
                //-------------处理高亮end
                //添加到集合中
                voList.add(vo);
            }
        }
        //返回结果
        return PageResponseResult.okResult(total, voList);
    }


    //抽取条件Dsl主张
    private void buildDSL(PageRequest pageRequest, CoursePubIndexDto dto, SearchRequest request) {
        //2.准备DSL
        //2-1query查询
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        //关键字
        if (StrUtil.isNotEmpty(dto.getKeyword())) {
            boolQuery.must(QueryBuilders.matchQuery("all", dto.getKeyword()));
        } else {
            boolQuery.must(QueryBuilders.matchAllQuery());
        }
        // 条件过滤
        // 一级分类
        if (StrUtil.isNotEmpty(dto.getMt())) {
            boolQuery.filter(QueryBuilders.termQuery("mt", dto.getMt()));
        }
        // 二级分类
        if (StrUtil.isNotEmpty(dto.getSt())) {
            boolQuery.filter(QueryBuilders.termQuery("st", dto.getSt()));
        }
        // 课程等级
        if (StrUtil.isNotEmpty(dto.getGrade())) {
            boolQuery.filter(QueryBuilders.termQuery("grade", dto.getGrade()));
        }

        // 条件设置到请求中
        request.source().query(boolQuery);
        //2-2分页
        Integer pageNo = pageRequest.getPageNo();
        Integer pageSize = pageRequest.getPageSize();
        Integer integer = (pageNo - 1) * pageSize;
        //2-3高亮
        request.source().highlighter(new HighlightBuilder().field("name").preTags("<font style = 'color:red'>").postTags("</font>").requireFieldMatch(false));
    }

    @Override
    public void removeDocument(Long coursePubId) throws IOException {
        //删除数据
        DeleteRequest request = new DeleteRequest();
        request.index("course").id(coursePubId.toString());
        client.delete(request, RequestOptions.DEFAULT);
    }
}